package com.doyled.jump.user.ai;

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;

import com.doyled.jump.object.Horsemen;
import com.doyled.jump.object.Spot;
import com.doyled.jump.user.PlayerAI;
import com.doyled.jump.util.ChessKind;
import com.doyled.jump.util.HorseState;
import com.doyled.jump.util.TeamInfo;

public class NewAI {
	
	TeamInfo teamInfo;
	PlayerAI player;
	int diceNumber;
	int gateSpotId;
	int startSpotId;
	
	public void setDiceNumber(int diceNumber) {
		this.diceNumber = diceNumber;
	}

	public NewAI(PlayerAI player, TeamInfo teamInfo) {
		this.player = player;
		this.teamInfo = teamInfo;
		this.gateSpotId = teamInfo.getGateSpot().getId();
		this.startSpotId = teamInfo.getStartSpot().getId();
	}
	
	public void decide(){
		Horsemen[] teamHorses = teamInfo.getHorses(); //team's horses
		List<Horsemen> movableHorses = getMovableHorses(); //movable horses in team at this time
		List<Horsemen> horsesInRoad = player.getHorsesInRoad();
		int[] horsesMark = new int[movableHorses.size()];
		BoardInfo beforeMove = null;
		BoardInfo afterMove = null;
		
		if(movableHorses.isEmpty()){
			player.releaseHorse();
			return;
		}
		
		//try to move horse in destination
		boolean result = moveHorseInDestination(movableHorses);
		if(result) return;
		
		//find a move
		Horsemen nearestGateHorse = movableHorses.get(0);
		int nearestGateHorseId = nearestGateHorse.getSpot().getId();
		int[] firstModelBoard = createBoard(horsesInRoad);
		//get to the destination in the emergency situation
		if(!isSafe(nearestGateHorseId, firstModelBoard) && nearestGateHorseId == gateSpotId){
			player.moveHorse(nearestGateHorse);
		}
		//find the best move to do
		else{
			for(int i = 0; i < movableHorses.size(); i++){
				int[] modelBoard = createBoard(horsesInRoad);	//create model board
				beforeMove = determineInfo(modelBoard, teamHorses);	//detemine model board information
				afterMove = tryMove(movableHorses.get(i), modelBoard, beforeMove, teamHorses);
				Statistics stat = beforeMove.compare(afterMove);	//the change after moving
				horsesMark[i] = calculateMark(stat);
			}
		}
		int bestMove = getBestMove(horsesMark);	//best move
		int mark = 0;
		if(diceNumber == 1 | diceNumber == 6){
			if(firstModelBoard[startSpotId - 1] != 1){
				mark = 2;
			}
		}
		if(horsesMark[bestMove] > mark){
			player.moveHorse(movableHorses.get(bestMove));
		} else {
			player.releaseHorse();
		}
	}
	
	private boolean moveHorseInDestination(List<Horsemen> movableHorse){
		for (Horsemen horse : movableHorse) {
			if((horse.getState() == HorseState.InDestination)
					&& !player.getMovableSpots(horse).isEmpty()){
				player.moveHorse(horse);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * create a new model board
	 * @param horsesInRoad
	 * @return model board
	 */
	private int[] createBoard(List<Horsemen> horsesInRoad){
		ChessKind kind = teamInfo.getKind();
		int[] board = new int[48];
		
		for (int i = 0; i < board.length; i++) {
			board[i] = 0;
		}
		
		for (Horsemen horse : horsesInRoad) {
			int id = horse.getSpot().getId();
			if(horse.getKind() == kind){
				board[id - 1] = 1;
			} else {
				board[id - 1] = -1;
			}
		}
		return board;
	}
	
	/**
	 * do a try move and get new information of board
	 * @param horse
	 * @param board
	 * @param beforeMove
	 * @param teamHorses
	 * @return new information of board
	 */
	private BoardInfo tryMove(Horsemen horse, int[] board, BoardInfo beforeMove, Horsemen[] teamHorses) {
		int lastSpotId = teamInfo.getGateSpot().getId();
		BoardInfo afterMove = beforeMove.clone();
		int currentHorseId = horse.getSpot().getId();
		int newHorseId = currentHorseId + diceNumber;
		newHorseId = (newHorseId > 48) ? (newHorseId - 48) : newHorseId;
		
		if((currentHorseId == lastSpotId) && (diceNumber > 4 && diceNumber <= 6)){
			afterMove.setGetInHighDes(true);
		}
		
		if(board[newHorseId - 1] == -1){	//an enemy horse is here
			afterMove.setEnemyHores(afterMove.getEnemyHores() - 1); //kick an enemy
		}
		
		if(newHorseId == lastSpotId){
			afterMove.setGetIntoGate(1); //a team's horse will be in gate
		}
		
		afterMove.setMovingNomal(true);
		
		int unsafeHorses = 0;		//find current unsafe horses
		for(Horsemen teamHorse : teamHorses){
			if(!isSafe(teamHorse.getSpot().getId(), board)){
				unsafeHorses++;
			}
		}
		afterMove.setUnSafe(unsafeHorses);
		return afterMove;
	}
	
	/**
	 * detemine the information of the board
	 * @param board
	 * @param boardInfo
	 * @param teamHorses
	 */
	private BoardInfo determineInfo(int[] board, Horsemen[] teamHorses) {
		BoardInfo boardInfo = new BoardInfo();
		int teamNum = 0;
		int enemyNum = 0;
		
		//get the team's horse quantity and enemy's horse quantity
		for(int i = 0; i < board.length; i++){
			if(board[i] == 1){
				teamNum++;
			} else if(board[i] == -1) {
				enemyNum ++;
			}
		}
		
		int teamHorseInSpot = (board[gateSpotId - 1] == 1) ? 1 : 0;	//team's horse in the gate spot
		
		int unsafeHorses = 0;	//unsafe horse quantity
		for (Horsemen horse : teamHorses) {
			if(!isSafe(horse.getSpot().getId(), board)){
				unsafeHorses++;
			}
		}
		
		boardInfo.setTeamHorses(teamNum);
		boardInfo.setEnemyHores(enemyNum);
		boardInfo.setGetIntoGate(teamHorseInSpot);
		boardInfo.setUnSafe(unsafeHorses);
		
		return boardInfo;
	}
	
	private int getBestMove(int[] horsesMark){
		int max = 0;
		int i = 0;
		for(; i < horsesMark.length; i++){
			int mark = horsesMark[i];
			max = (mark > max) ? mark : max;
		}
		for(i = 0; i < horsesMark.length; i++){
			int mark = horsesMark[i];
			if(mark == max) break;
		}
		return i;
	}
	
	private int calculateMark(Statistics stat) {
		int mark = 0;
		if(stat.isHorseInGate()) mark += 50;
		if(stat.isKickedEnemy()) mark += 10;
		if(stat.isSavedHorse()) mark += 10;
		if(stat.isGetInHighDes()) mark += 100;
		if(stat.isGetInLowDes()) mark += 5;
		if(stat.isMovingNormal()) mark += 1;
		return mark;
	}
	
	/**
	 * check that this horse is safe or not
	 * @param horseIndex	index of horse in model board
	 * @param board
	 * @return	safe: true
	 * 			unsafe: false
	 */
	private boolean isSafe(int horseIndex, int[] board){
		int fromIndex =  horseIndex - 6;
		if(fromIndex < 1) return true;
		for(int i = fromIndex; i < horseIndex; i++){
			if(board[i - 1] == -1) return false;
		}
		return true;
	}
	
	/**
	 * get our team's horses can move
	 * @return list of horse (the nearest horse by gate spot will be the first value in list)
	 */
	private List<Horsemen> getMovableHorses(){
		List<Horsemen> movableHorses = new LinkedList<Horsemen>();
		Horsemen[] teamHorses = teamInfo.getHorses();
		for(Horsemen horse : teamHorses){
			if(player.getMovableSpots(horse).size() != 0){
				movableHorses.add(horse);
			}
		}
		Collections.sort(movableHorses, new Comparator<Horsemen>() {
			@Override
			public int compare(Horsemen o1, Horsemen o2) {
				int currentSpotId1 = o1.getSpot().getId();
				int currentSpotId2 = o2.getSpot().getId();
				if(o1.getKind() != ChessKind.Red){
					if(currentSpotId1 <= gateSpotId) currentSpotId1 += 48;
					if(currentSpotId2 <= gateSpotId) currentSpotId2 += 48;
				}
				int returnValue = 0;
				if(currentSpotId1 > currentSpotId2) returnValue = -1;
				else if(currentSpotId1 < currentSpotId2) returnValue = 1;
				else returnValue = 0;
				return returnValue;
			}
		});
		return movableHorses;
	}

	class BoardInfo {
		private int teamHorses;
		private int enemyHores;
		private int getIntoGate;
		private int unSafe;	
		private boolean releaseHorse;
		private boolean getInHighDes;
		private boolean getInLowDes;
		private boolean movingNomal;
		

		public boolean isMovingNomal() {
			return movingNomal;
		}

		public void setMovingNomal(boolean movingNomal) {
			this.movingNomal = movingNomal;
		}

		public int getTeamHorses() {
			return teamHorses;
		}

		public void setTeamHorses(int teamHorses) {
			this.teamHorses = teamHorses;
		}

		public int getEnemyHores() {
			return enemyHores;
		}

		public void setEnemyHores(int enemyHores) {
			this.enemyHores = enemyHores;
		}

		public int getGetIntoGate() {
			return getIntoGate;
		}

		public void setGetIntoGate(int getIntoGate) {
			this.getIntoGate = getIntoGate;
		}

		public int getUnSafe() {
			return unSafe;
		}

		public void setUnSafe(int unSafe) {
			this.unSafe = unSafe;
		}

		public BoardInfo() {
			this.teamHorses = this.enemyHores = this.getIntoGate = this.unSafe = 0;
		}
		
		public boolean isReleaseHorse() {
			return releaseHorse;
		}

		public void setReleaseHorse(boolean releaseHorse) {
			this.releaseHorse = releaseHorse;
		}

		public boolean isGetInHighDes() {
			return getInHighDes;
		}

		public void setGetInHighDes(boolean getInHighDes) {
			this.getInHighDes = getInHighDes;
		}

		public boolean isGetInLowDes() {
			return getInLowDes;
		}

		public void setGetInLowDes(boolean getInLowDes) {
			this.getInLowDes = getInLowDes;
		}

		public BoardInfo clone(){
			BoardInfo cloneBoardInfo = new BoardInfo();
			cloneBoardInfo.setTeamHorses(teamHorses);
			cloneBoardInfo.setEnemyHores(enemyHores);
			cloneBoardInfo.setGetIntoGate(getIntoGate);
			cloneBoardInfo.setUnSafe(unSafe);
			cloneBoardInfo.setReleaseHorse(releaseHorse);
			cloneBoardInfo.setMovingNomal(movingNomal);
			cloneBoardInfo.setGetInLowDes(getInLowDes);
			cloneBoardInfo.setGetInHighDes(getInHighDes);
			return cloneBoardInfo;
		}
		
		public Statistics compare(BoardInfo info){
			Statistics stat = new Statistics();
			int teamDifferent = info.getTeamHorses() - this.teamHorses;	//difference of team's horses between before moving and after moving
			int enemyDifferent = info.getEnemyHores() - this.enemyHores; //difference of team's horses between before moving and after moving
			int horseInGate = info.getGetIntoGate() - this.getIntoGate;	//horse are in gate spot
			int unsafeHorse = info.getUnSafe() - this.unSafe;	//difference of number of horses which are in unsafe
			stat.setKickedEnemy((enemyDifferent != 0));
			stat.setReleasedHorse((teamDifferent != 0));
			stat.setSavedHorse((unsafeHorse != 0));
			stat.setHorseInGate(horseInGate != 0); 
			stat.setReleasedHorse(this.releaseHorse || info.isReleaseHorse());
			stat.setGetInLowDes(this.getInLowDes || info.isGetInLowDes());
			stat.setGetInHighDes(this.getInHighDes || info.isGetInHighDes());
			stat.setMovingNormal(this.movingNomal || info.isMovingNomal());
			return stat;
		}
	}
	
	class Statistics{
		private boolean releasedHorse;
		private boolean kickedEnemy;
		private boolean horseInGate;
		private boolean savedHorse;
		private boolean movingNormal;
		private boolean getInHighDes;
		private boolean getInLowDes;
		
		
		public boolean isMovingNormal() {
			return movingNormal;
		}

		public void setMovingNormal(boolean movingNormal) {
			this.movingNormal = movingNormal;
		}

		public boolean isReleasedHorse() {
			return releasedHorse;
		}

		public void setReleasedHorse(boolean releasedHorse) {
			this.releasedHorse = releasedHorse;
		}

		public boolean isKickedEnemy() {
			return kickedEnemy;
		}

		public void setKickedEnemy(boolean kickedEnemy) {
			this.kickedEnemy = kickedEnemy;
		}

		public boolean isHorseInGate() {
			return horseInGate;
		}

		public void setHorseInGate(boolean horseInGate) {
			this.horseInGate = horseInGate;
		}

		public boolean isSavedHorse() {
			return savedHorse;
		}

		public void setSavedHorse(boolean savedHorse) {
			this.savedHorse = savedHorse;
		}

		public boolean isGetInHighDes() {
			return getInHighDes;
		}

		public void setGetInHighDes(boolean getInHighDes) {
			this.getInHighDes = getInHighDes;
		}

		public boolean isGetInLowDes() {
			return getInLowDes;
		}

		public void setGetInLowDes(boolean getInLowDes) {
			this.getInLowDes = getInLowDes;
		}

		public Statistics() {
			this.releasedHorse = this.kickedEnemy = this.savedHorse = this.horseInGate = this.getInLowDes = this.getInHighDes = this.movingNormal = false;
		}
	}
}
